#include "expression.h"

namespace logo
{
	Ptr<Expression>  GetExpression(List<Token>& tokens, zint& index, Dictionary<std::wstring, AssignmentExpression>& env)
	{
		List<ExpressionError> errs;
		Ptr<Expression> expression = nullptr;
		zint tindex = index;
		expression = ParseLoopExpression(tokens, tindex, env, errs);
		if (expression != nullptr&&errs.size() == 0)
		{
			index = tindex;
			return expression;
		}
		tindex = index;
		errs.clear();
		expression = ParseSelectExpression(tokens, tindex, env, errs);
		if (expression != nullptr&&errs.size() == 0)
		{
			index = tindex;
			return expression;
		}
		tindex = index;
		errs.clear();
		expression = ParseCmdExpression(tokens, tindex, env, errs);
		if (expression != nullptr&&errs.size() == 0)
		{
			index = tindex;
			return expression;
		}
		tindex = index;
		errs.clear();
		expression = ParseUseFuncExpression(tokens, tindex, env, errs);
		if (expression != nullptr&&errs.size() == 0)
		{
			index = tindex;
			return expression;
		}
		tindex = index;
		errs.clear();
		expression = ParseAssignmentExpression(tokens, tindex, env, errs);
		if (expression != nullptr&&errs.size() == 0)
		{
			index = tindex;
			return expression;
		}
		tindex = index;
		errs.clear();
		expression = ParseCompareExpression(tokens, tindex, env, errs);
		if (expression != nullptr&&errs.size() == 0)
		{
			index = tindex;
			return expression;
		}
		tindex = index;
		errs.clear();
		expression = ParseArithmeticExpression(tokens, tindex, env, errs);
		if (expression != nullptr&&errs.size() == 0)
		{
			index = tindex;
			return expression;
		}
		return expression;
	}

	bool        ParseUnit(List<Token>& tokens, zint& index, List<Ptr<Expression>>& expressions,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs)
	{
		while (index<tokens.size())
		{
			Ptr<Expression> expression = GetExpression(tokens, index, env);
			if (expression == nullptr)
			{
				break;
			}
			expressions.add(expression);
			if (index<tokens.size())
			{
				if (tokens[index].type == tokentype::SEP)
				{
					index++;
				}
			}
		}
		return false;
	}

	Ptr<ArithmeticExpression> ParseArithmeticExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env, List<ExpressionError>& errs)
	{
		if (errs.size()>0) return nullptr;
		Ptr<ArithmeticExpression>  expression = ParseFactor(tokens, index, env, errs);
		Ptr<ArithmeticExpression>  exp;
		while (expression != nullptr&&index<tokens.size())
		{
			if (tokens[index].type == tokentype::ADD)
			{
				index++;
				if (index == tokens.size())
				{
					ExpressionError err;
					err.info = tokens[index - 1];
					err.message = L"ArithmeticExpression format error.";
					errs.add(err);
					return nullptr;
				}
				exp = ParseFactor(tokens, index, env, errs);
				if (exp != nullptr)
				{
					expression->value += exp->value;
				}
				else break;
			}
			else if (tokens[index].type == tokentype::SUB)
			{
				index++;
				if (index == tokens.size())
				{
					ExpressionError err;
					err.info = tokens[index - 1];
					err.message = L"ArithmeticExpression format error.";
					errs.add(err);
					return nullptr;
				}
				exp = ParseFactor(tokens, index, env, errs);
				if (exp != nullptr)
				{
					expression->value -= exp->value;
				}
				else break;
			}
			else break;
		}
		return expression;
	}

	Ptr<ArithmeticExpression> ParseTerm(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env, List<ExpressionError>& errs)
	{
		Ptr<ArithmeticExpression> expression = nullptr;
		if (index == tokens.size() || errs.size()>0)
		{
			return nullptr;
		}
		if (tokens[index].type == tokentype::NUMERICAL)
		{
			expression = new ArithmeticExpression;
			expression->value = (double)(wtof(tokens[index].value));
			index++;
			return expression;
		}
		else if (tokens[index].type == tokentype::VAR)
		{
			List<std::wstring> name;
			name = from(env.keys()).where([&](std::wstring e){return e == tokens[index].value; }).to_zl_list();
			if (name.size()>0)
			{
				expression = new ArithmeticExpression;
				expression->value = env[tokens[index].value].value;
				index++;
				return expression;
			}
			else
			{
				ExpressionError err;
				err.info = tokens[index];
				err.message = tokens[index].value + L"is not exit";
				errs.add(err);
				return nullptr;
			}
		}
		else
		{
			if (tokens[index].type == tokentype::OPENBRACKETS)
			{
				index++;
				expression = ParseArithmeticExpression(tokens, index, env, errs);
				if (index == tokens.size())
				{
					ExpressionError err;
					err.info = tokens[index - 1];
					err.message = L"missing right brackets.";
					errs.add(err);
					return nullptr;
				}
				else
				{
					if (tokens[index].type == tokentype::CLOSEBRACKETS)
					{
						index++;
						return expression;
					}
					else
					{
						ExpressionError err;
						err.info = tokens[index];
						err.message = L"missing right brackets.";
						errs.add(err);
						return nullptr;
					}
				}
			}
			else if (tokens[index].type == tokentype::SEP)
			{
				return expression;
			}
			else
			{
				ExpressionError err;
				err.info = tokens[index];
				err.message = L"ArithmeticExpression error.";
				errs.add(err);
				return nullptr;
			}
		}
	}

	Ptr<ArithmeticExpression> ParseFactor(List<Token>& tokens, zint& index, Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs)
	{
		if (errs.size()>0) return nullptr;
		Ptr<ArithmeticExpression> expression = ParseTerm(tokens, index, env, errs);
		Ptr<ArithmeticExpression>  exp;
		while (expression != nullptr&&index<tokens.size())
		{
			if (tokens[index].type == tokentype::MUL)
			{
				index++;
				if (index == tokens.size())
				{
					ExpressionError err;
					err.info = tokens[index - 1];
					err.message = L"ArithmeticExpression format error.";
					errs.add(err);
					return nullptr;
				}
				exp = ParseTerm(tokens, index, env, errs);
				if (exp != nullptr)
				{
					expression->value *= exp->value;
				}
				else break;
			}
			else if (tokens[index].type == tokentype::DIV)
			{
				index++;
				if (index == tokens.size())
				{
					ExpressionError err;
					err.info = tokens[index - 1];
					err.message = L"ArithmeticExpression format error.";
					errs.add(err);
					return nullptr;
				}
				exp = ParseTerm(tokens, index, env, errs);
				if (exp != nullptr)
				{
					expression->value /= exp->value;
				}
				else break;
			}
			else break;
		}
		return expression;
	}

	Ptr<Expression> ParseAssignmentExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env, List<ExpressionError>& errs)
	{
		if (errs.size()>0 || index == tokens.size()) return nullptr;
		AssignmentExpression*  expression = nullptr;
		if (tokens[index].type == tokentype::VAR)
		{
			std::wstring name = tokens[index].value;
			index++;
			if (index == tokens.size() || tokens[index].type != tokentype::SEP)
			{
				ExpressionError err;
				err.info = tokens[index == tokens.size() ? index - 1 : index];
				err.message = L"assignment error.";
				errs.add(err);
				return nullptr;
			}
			index++;
			Ptr<ArithmeticExpression> result = ParseArithmeticExpression(tokens, index, env, errs);
			if (result == nullptr)  return nullptr;
			expression = new AssignmentExpression;
			expression->name = name;
			expression->value = result->value;
		}
		return expression;
	}

	Ptr<Expression> ParseCmdExpression(List<Token>& tokens, zint& index, Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs)
	{
		if (errs.size()>0 || index == tokens.size()) return nullptr;
		CmdExpression* expression = nullptr;
		List<ExpressionError> es;
		if (tokens[index].type == tokentype::CMD)
		{
			expression = new CmdExpression;
			expression->name = tokens[index].value;
			index++;
			while (index<tokens.size())
			{
				if (tokens[index].type == tokentype::SEP)
				{
					index++;
					if (index == tokens.size()) break;
					Ptr<ArithmeticExpression> arg;
					if (tokens[index].type != tokentype::CMD)
					{
						//arg=ParseArithmeticExpression(tokens,index,env,errs);
						arg = ParseArithmeticExpression(tokens, index, env, es);
						if (arg != nullptr)
						{
							expression->args.add(arg->value);
						}
						else
						{
							//index--; 
							break;
						}
					}
					else break;
				}
				else
				{
					ExpressionError err;
					err.info = tokens[index];
					err.message = L"cmd format error.";
					errs.add(err);
					break;
				}
			}
		}
		return expression;
	}

	Ptr<Expression>   ParseUseFuncExpression(List<Token>& tokens, zint& index, Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs)
	{
		if (errs.size()>0 || tokens.size() == 0) return nullptr;
		FuncExpression* expression = nullptr;
		List<ExpressionError> es;
		if (tokens[index].type == tokentype::DEF)
		{
			expression = new FuncExpression;
			expression->name = tokens[index].value;
			index++;
			while (index<tokens.size())
			{
				if (tokens[index].type == tokentype::SEP)
				{
					index++;
					Ptr<ArithmeticExpression> arg;
					if (index == tokens.size()) break;
					if (tokens[index].type != tokentype::DEF)
					{
						//arg=ParseArithmeticExpression(tokens,index,env,errs);
						arg = ParseArithmeticExpression(tokens, index, env, es);
						if (arg != nullptr)
						{
							expression->args.add(arg->value);
						}
						else
						{
							//index--; 
							break;
						}
					}
					else break;
				}
				else
				{
					ExpressionError err;
					err.info = tokens[index];
					err.message = L"func format error.";
					errs.add(err);
					break;
				}
			}
		}
		return expression;
	}

	Ptr<Expression>   ParseCompareExpression(List<Token>& tokens, zint& index, Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs)
	{
		if (errs.size()>0) return nullptr;
		CompareExpression* expression = nullptr;
		Ptr<ArithmeticExpression> left = ParseArithmeticExpression(tokens, index, env, errs);
		if (left == nullptr) return nullptr;
		if (index == tokens.size())
		{
			ExpressionError err;
			err.info = tokens[index - 1];
			err.message = L"compareexpression format error.";
			errs.add(err);
			return nullptr;
		}
		if (tokens[index].type == tokentype::LT)
		{
			index++;
			if (index == tokens.size())
			{
				ExpressionError err;
				err.info = tokens[index - 1];
				err.message = L"compareexpression format error.";
				errs.add(err);
				return nullptr;
			}
			Ptr<ArithmeticExpression> right = ParseArithmeticExpression(tokens, index, env, errs);
			if (right == nullptr) return nullptr;
			expression = new CompareExpression;
			expression->value = left->value<right->value ? true : false;
		}
		else if (tokens[index].type == tokentype::GT)
		{
			index++;
			if (index == tokens.size())
			{
				ExpressionError err;
				err.info = tokens[index - 1];
				err.message = L"compareexpression format error.";
				errs.add(err);
				return nullptr;
			}
			Ptr<ArithmeticExpression> right = ParseArithmeticExpression(tokens, index, env, errs);
			if (right == nullptr) return nullptr;
			expression = new CompareExpression;
			expression->value = left->value>right->value ? true : false;
		}
		else if (tokens[index].type == tokentype::LE)
		{
			index++;
			if (index == tokens.size())
			{
				ExpressionError err;
				err.info = tokens[index - 1];
				err.message = L"compareexpression format error.";
				errs.add(err);
				return nullptr;
			}
			Ptr<ArithmeticExpression> right = ParseArithmeticExpression(tokens, index, env, errs);
			if (right == nullptr) return nullptr;
			expression = new CompareExpression;
			expression->value = left->value <= right->value ? true : false;
		}
		else if (tokens[index].type == tokentype::GE)
		{
			index++;
			if (index == tokens.size())
			{
				ExpressionError err;
				err.info = tokens[index - 1];
				err.message = L"compareexpression format error.";
				errs.add(err);
				return nullptr;
			}
			Ptr<ArithmeticExpression> right = ParseArithmeticExpression(tokens, index, env, errs);
			if (right == nullptr) return nullptr;
			expression = new CompareExpression;
			expression->value = left->value >= right->value ? true : false;
		}
		else if (tokens[index].type == tokentype::EQ)
		{
			index++;
			if (index == tokens.size())
			{
				ExpressionError err;
				err.info = tokens[index - 1];
				err.message = L"compareexpression format error.";
				errs.add(err);
				return nullptr;
			}
			Ptr<ArithmeticExpression> right = ParseArithmeticExpression(tokens, index, env, errs);
			if (right == nullptr) return nullptr;
			expression = new CompareExpression;
			expression->value = left->value == right->value ? true : false;
		}
		else
		{
			ExpressionError err;
			err.info = tokens[index];
			err.message = L"compareexpression format error.";
			errs.add(err);
		}
		return expression;
	}

	Ptr<Expression> ParseSelectExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs)
	{
		if (index == tokens.size() || errs.size()>0) return nullptr;
		SelectExpression* expression = nullptr;
		if (tokens[index].type == tokentype::KEYWORD&&tokens[index].value == L"IF")
		{
			index++;
			if (index == tokens.size())
			{
				ExpressionError e;
				e.info = tokens[index];
				e.message = L"selectexpression format error.";
				errs.add(e);
				return expression;
			}
			if (tokens[index].type == tokentype::SEP)
			{
				index++;
				if (index == tokens.size())
				{
					ExpressionError e;
					e.info = tokens[index];
					e.message = L"selectexpression format error.";
					errs.add(e);
					return expression;
				}
				Ptr<Expression> exp = ParseCompareExpression(tokens, index, env, errs);
				if (exp == nullptr) return expression;
				expression = new SelectExpression;
				CompareExpression* p = dynamic_cast<CompareExpression*>(exp.Obj());
				expression->value = p->value;
				if (index == tokens.size())
				{
					ExpressionError e;
					e.info = tokens[index - 1];
					e.message = L"selectexpression format error.";
					errs.add(e);
					return expression;
				}
				if (tokens[index].type != tokentype::SEP)
				{
					ExpressionError e;
					e.info = tokens[index];
					e.message = L"selectexpression format error.";
					errs.add(e);
					return expression;
				}
				index++;
				ParseUnit(tokens, index, expression->tbranch, env, errs);
				if (index<tokens.size() && tokens[index].value == L"ELSE")
				{
					index++;
					while (tokens[index].type == tokentype::SEP) index++;
					ParseUnit(tokens, index, expression->fbranch, env, errs);
				}
			}
		}
		return expression;
	}

	Ptr<Expression> ParseLoopExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs)
	{
		if (index == tokens.size() || errs.size()>0) return nullptr;
		LoopExpression* expression = nullptr;
		if (tokens[index].value == L"REPEAT")
		{
			index++;
			zint loops = 0;
			while (index<tokens.size() && tokens[index].type == tokentype::SEP) index++;
			if (index == tokens.size())
			{
				ExpressionError e;
				e.info = tokens[index - 1];
				e.message = L"loopexpression format error.";
				errs.add(e);
				return expression;
			}
			if (tokens[index].type == tokentype::NUMERICAL)
			{
				loops = wtoi(tokens[index].value);
				index++;
				if (index == tokens.size())
				{
					ExpressionError e;
					e.info = tokens[index - 1];
					e.message = L"loopexpression format error.";
					errs.add(e);
					return expression;
				}
				if (tokens[index].type == tokentype::OPENMIDBRACKETS)
				{
					index++;
					if (index == tokens.size() || tokens[index].type != tokentype::SEP)
					{
						ExpressionError e;
						e.info = tokens[index - 1];
						e.message = L"loopexpression format error.";
						errs.add(e);
						return expression;
					}
					index++;
					if (index == tokens.size())
					{
						ExpressionError e;
						e.info = tokens[index - 1];
						e.message = L"loopexpression format error.";
						errs.add(e);
						return expression;
					}
					expression = new LoopExpression;
					expression->loops = loops;
					ParseUnit(tokens, index, expression->proc, env, errs);
					if (index == tokens.size() || tokens[index].type != tokentype::CLOSEMIDBRACKETS)
					{
						ExpressionError e;
						e.info = tokens[index - 1];
						e.message = L"loopexpression format error.";
						errs.add(e);
						delete expression;
						expression = nullptr;
						return expression;
					}
				}
				else
				{
					ExpressionError e;
					e.info = tokens[index - 1];
					e.message = L"loopexpression format error.";
					errs.add(e);
					return expression;
				}
			}
			else
			{
				ExpressionError e;
				e.info = tokens[index];
				e.message = L"loopexpression format error.";
				errs.add(e);
				return expression;
			}
		}
		return expression;
	}

	Ptr<Expression> ParseDefFuncExpression(List<CodeLine>& lines, zint& rows, Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs)
	{
		if (rows == lines.size()) return nullptr;
		DefFuncExpression* expression = nullptr;
		zint colindex = 0;
		if (lines[rows].tokens[colindex].value == L"TO")
		{
			colindex++;
			if (colindex == lines[rows].tokens.size() || lines[rows].tokens[colindex].type != tokentype::SEP)
			{
				ExpressionError e;
				e.info = lines[rows].tokens[colindex == lines[rows].tokens.size() ? colindex-- : colindex];
				e.message = L"deffuncexpression format error.";
				errs.add(e);
				return expression;
			}
			colindex++;
			if (colindex == lines[rows].tokens.size() || lines[rows].tokens[colindex].type != tokentype::DEF)
			{
				ExpressionError e;
				e.info = lines[rows].tokens[colindex == lines[rows].tokens.size() ? colindex-- : colindex];
				e.message = L"deffuncexpression format error.";
				errs.add(e);
				return expression;
			}
			expression = new DefFuncExpression;
			expression->name = lines[rows].tokens[colindex].value;
			expression->start = rows + 1;
			colindex++;
			while (colindex<lines[rows].tokens.size())
			{
				if (lines[rows].tokens[colindex].type == tokentype::VAR)
				{
					expression->args.add(lines[rows].tokens[colindex].value);
					colindex++;
				}
				else if (lines[rows].tokens[colindex].type == tokentype::SEP)
				{
					colindex++;
				}
				else
				{
					ExpressionError e;
					e.info = lines[rows].tokens[colindex];
					e.message = L"deffuncexpression format error.";
					errs.add(e);
					delete expression;
					expression = nullptr;
					return expression;
				}
			}
			for (zint i = rows + 1; i<lines.size(); i++)
			{
				List<Token> tlist;
				tlist = from(lines[i].tokens).where([&](Token token){
					return (token.type == tokentype::SEP || token.value == L"END");
				}).to_zl_list();
				if (tlist.size() == lines[i].tokens.size())
				{
					expression->end = i;
					return expression;
				}
			}
			delete expression;
			expression = nullptr;
			ExpressionError e;
			e.info = lines[rows].tokens[colindex - 1];
			e.message = L"deffuncexpression format error.";
			errs.add(e);
		}
		return expression;
	}

	bool      ParseArea(List<CodeLine>& lines, zint& rows, List<Ptr<Expression>>& expressions,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs)
	{
		while (rows<lines.size())
		{
			if (errs.size()>0) return false;
			Ptr<Expression> exp = ParseDefFuncExpression(lines, rows, env, errs);
			if (exp != nullptr&&errs.size() == 0)
			{
				DefFuncExpression* ptr = dynamic_cast<DefFuncExpression*>(exp.Obj());
				rows = ptr->end + 1;
				expressions.add(exp);
			}
			else if (exp == nullptr&&errs.size()>0)
			{
				return false;
			}
			else
			{
				zint cols = 0;
				ParseUnit(lines[rows].tokens, cols, expressions, env, errs);
				rows++;
			}
		}
		return true;
	}
};
